<!doctype html>
<html lang="zh-CN">
<head>

    <meta charset="utf-8">
    <meta name="generator" content="Hugo 0.74.1" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Flutter FocusNode 焦点那点事-(二) | caijinglong的博客</title>
    <meta property="og:title" content="Flutter FocusNode 焦点那点事-(二) - caijinglong的博客">
    <meta property="og:type" content="article">
        
    <meta property="article:published_time" content="2020-07-22T09:29:11&#43;08:00">
        
        
    <meta property="article:modified_time" content="2020-07-22T09:29:11&#43;08:00">
        
    <meta name="Keywords" content="golang,go语言,flutter,caijinglong,java,android,博客,项目管理,python,软件架构,公众号,小程序">
    <meta name="description" content="Flutter FocusNode 焦点那点事-(二)">
        
    <meta name="author" content="caijinglong">
    <meta property="og:url" content="http://www.kikt.top/posts/flutter/focusnode-2/">
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

    <link rel="stylesheet" href="/css/normalize.css">
    
        <link rel="stylesheet" href="/css/prism.css">
    
    <link rel="stylesheet" href="/css/style.css">
    <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>

    

   
    <script>
        (function(){
            var bp = document.createElement('script');
            var curProtocol = window.location.protocol.split(':')[0];
            if (curProtocol === 'https') {
                bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
            }
            else {
                bp.src = 'http://push.zhanzhang.baidu.com/push.js';
            }
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(bp, s);
        })();
    </script>

    
    
</head>

<body>
<header id="header" class="clearfix">
    <div class="container">
        <div class="col-group">
            <div class="site-name ">
                
                    <a id="logo" href="http://www.kikt.top">
                        caijinglong的博客
                    </a>
                
                <p class="description">Android/Flutter开发者，对于golang/python/ios/java 均有所了解</p>
            </div>
            <div>
                <nav id="nav-menu" class="clearfix">
                    <a class="" href="http://www.kikt.top">首页</a>
                    
                    <a  href="http://www.kikt.top/archives/" title="归档">归档</a>
                    
                    <a  href="http://www.kikt.top/contact/" title="接单">接单</a>
                    
                    <a  href="http://www.kikt.top/about/" title="关于">关于</a>
                    
                </nav>
            </div>
        </div>
    </div>
</header>


<div id="body">
    <div class="container">
        <div class="col-group">

            <div class="col-8" id="main">
                <div class="res-cons">
                    <article class="post">
                        <header>
                            <h1 class="post-title">Flutter FocusNode 焦点那点事-(二)</h1>
                        </header>
                        <date class="post-meta meta-date">
                            2020年7月22日
                        </date>
                        
                        <div class="post-meta meta-category">
                            |
                            
                                <a href="http://www.kikt.top/categories/flutter">flutter</a>
                            
                        </div>
                        
                        
                        <div class="post-meta">
                            <span id="busuanzi_container_page_pv">|<span id="busuanzi_value_page_pv"></span><span> 阅读</span></span>
                        </div>
                        
                        <div class="post-content">
                            <p>上一章, 简单的使用了一下 <code>FocusNode</code> 和周边的一些东西, 今天来扒一扒 <code>FocusNode</code> 整体的附着(attach)和分离(detach)<br>
flutter 环境还是针对 1.17.5</p>
<h2 id="focus-家族的源码分析">Focus 家族的源码分析</h2>
<h3 id="附着">附着</h3>
<p>首先第一, 平时是以 <code>FocusNode</code> 为主要对象的, 那么这东西是怎么附着到整体的呢</p>
<p>构造方法</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595382471.png" alt="1595382471"></p>
<ul>
<li><code>debugLabel</code>, 这东西看名字就是 debug 用的, 先不管</li>
<li><code>onKey</code>, 一看就是接受事件用的<code>RawKeyEvent</code></li>
<li><code>canRequestFocus</code>, 就是能不能接受焦点</li>
<li><code>skipTraversal</code>, 是否接受遍历</li>
</ul>
<p>我们再找找方法</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595381640.png" alt="1595381640"></p>
<p>有几个可能用到的关键字段, 我们一个个看, 但是不一定会用的到, 但是有所了解也是好</p>
<ul>
<li>
<p><code>context</code>, 这个是和控件结合 的, 单纯看这里还看不出来</p>
</li>
<li>
<p><code>_manager</code>, 我们知道这个在 flutter 环境中其实是全局单例的, 但是这里为了解耦所以可能是哪里传进来的, 我们理论上可以忽略</p>
</li>
<li>
<p><code>_ancestors</code>, 嗯.. 没有注释, 那就只能看看源码了
<img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595381928.png" alt="1595381928">
所以, 这个东西是把所有的父节点都装到了这个里. 嗯, 个人经验,应用层的话大概率用不上.</p>
</li>
<li>
<p><code>_descendants</code>,这东西也是个三无, 没事
<img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595382072.png" alt="1595382072">
都在源码里了, 这东西是深度为 1 的子 <code>node</code> 的 <code>descendants</code> 和子<code>node</code>, 然后里面递归了&hellip; 换句话说, 其实, 这东西是所有的子 <code>node</code></p>
</li>
<li>
<p><code>_hasKeyboardToken</code> 这东西暂时没看出作用, 先放放</p>
</li>
<li>
<p><code>children</code>, 这东西就是所有子<code>node</code>, 怎么附着的暂时还不知道, 先放放</p>
</li>
<li>
<p><code>traversalChildren</code>, 这就是上次用到的那东西的核心了, 就是靠这东西筛选了一下, 可以接受遍历的子节点, 这里就和构造方法那对上了
<img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595382613.png" alt="1595382613"></p>
</li>
</ul>
<p>找了一些核心字段, 但是还是没找到怎么附着的, 我们整体看看方法名
<img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595382682.png" alt="1595382682">
好家伙, 一屏都要装不下了, 看看名字, 直奔 <code>attach</code>
<img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595382729.png" alt="1595382729"></p>
<p>这方法看起来就是附着的方法了, 咱们跟一下这个方法, 注意到其实内部只有绑定 <code>context</code>, 绑定 <code>key</code> 回调, 然后创建了一个 <code>attachment</code>, 这东西是何方神圣, 咱们来跟进去看看</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595396089.png" alt="1595396089"></p>
<p>这东西看介绍是用来保证 <code>FocusNode</code> 不会被多处附着的, 怎么实现的呢</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595396295.png" alt="1595396295">
<img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595396309.png" alt="1595396309">
好吧, 看上去就是检测一下自己位置对不对, 不对就重新找个父节点</p>
<h3 id="node-的树形结构">Node 的树形结构</h3>
<p>上面简单的看了一下源码的结构, 但是可能还是不能系统的想象到底有哪些 node</p>
<p>我写一个方法简单输出一下 <code>FocusNode</code> 的节点关系</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-dart" data-lang="dart"><span style="color:#66d9ef">import</span> <span style="color:#e6db74">&#39;package:flutter/material.dart&#39;</span>;

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">FocusNodePrintExample</span> <span style="color:#66d9ef">extends</span> StatefulWidget {
  <span style="color:#960050;background-color:#1e0010">@</span>override
  _FocusNodePrintExampleState createState() <span style="color:#f92672">=&gt;</span> _FocusNodePrintExampleState();
}

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">_FocusNodePrintExampleState</span> <span style="color:#66d9ef">extends</span> State<span style="color:#f92672">&lt;</span>FocusNodePrintExample<span style="color:#f92672">&gt;</span> {
  FocusNode node <span style="color:#f92672">=</span> FocusNode(debugLabel: <span style="color:#e6db74">&#39;the input&#39;</span>);
  FocusNode node2 <span style="color:#f92672">=</span> FocusNode(debugLabel: <span style="color:#e6db74">&#39;the raised button&#39;</span>);
  FocusNode node3 <span style="color:#f92672">=</span> FocusNode(debugLabel: <span style="color:#e6db74">&#39;the float button&#39;</span>);

  <span style="color:#960050;background-color:#1e0010">@</span>override
  Widget build(BuildContext context) {
    <span style="color:#66d9ef">return</span> Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          TextField(
            focusNode: node,
          ),
          RaisedButton(
            onPressed: () {},
            focusNode: node2,
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        focusNode: node3,
        onPressed: () {
          priceAllFocusNode();
        },
      ),
    );
  }
}

<span style="color:#66d9ef">void</span> printNode(<span style="color:#66d9ef">int</span> level, FocusNode node) {
  print(<span style="color:#e6db74">&#39;</span><span style="color:#e6db74">${</span><span style="color:#e6db74">&#39;--&#39;</span> <span style="color:#f92672">*</span> level<span style="color:#e6db74">}</span><span style="color:#e6db74"> </span><span style="color:#e6db74">$</span>node<span style="color:#e6db74">&#39;</span>);
  <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">final</span> child <span style="color:#66d9ef">in</span> node.children) {
    printNode(level <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, child);
  }
}

<span style="color:#66d9ef">void</span> priceAllFocusNode() {
  printNode(<span style="color:#ae81ff">0</span>, FocusManager.instance.rootScope);
}

</code></pre></div><p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595405011.png" alt="1595405011"></p>
<p>这样就比较清晰了, 一个页面, 即使在没有自己使用 <code>FocusScope</code> 组织的情况下也会有自己的 <code>FocusNode</code>, 然后所有子节点会&quot;自发&quot;的形成一个并列的 FocusNode 关系, 并附着到当前页的 <code>FocusNode</code> 上, 这个 <code>ModalScopeState</code>就是让每一个页面有自己的焦点区域, 从而不会互相影响</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595466436.png" alt="image-20200723090715817"></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595466445.png" alt="image-20200723090725399"></p>
<p>这两处就是这东西定义<code>FocusScope</code>的地方, 然后这东西本身的定义是在<code>ModalRoute</code>里面</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595466598.png" alt="image-20200723090958113"></p>
<p>而<code>ModalRoute</code>是各种常见的<code>PageRoute</code>(<code>MaterialPageRoute</code>,<code>CupertinoPageRoute</code>的父类),<code>PopupRoute</code>(<code>DialogRoute</code>的父类), 嗯,路由和页面的洗也串联起来了. 同时, 我们也知道为什么单独的 dialog 也好,页面也好, 焦点都是独立的</p>
<h3 id="改变焦点">改变焦点</h3>
<p>上面把焦点的区域等等东西探索完毕, 接下来看看改变焦点的时候都做了什么, <code>requestFocus</code>这个方法在整个 flutter 中只定义了一次</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1596098963.png" alt="image-20200723092332981"></p>
<p>可以看到, 这东西可以接受一个入参, 是另一个 node, 但对于这 node 有所限制, 比如属于当前 node 的父节点. 当然不传的话, 就是调用<code>_doRequestFocus</code>的事了, 我们再来看看这东西了做了什么</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595467553.png" alt="image-20200723092553734"></p>
<p>根据注释, 这东西在<code>scopeNode</code>里会被覆盖, 我们一会儿看覆盖的, 先看当前的, 比如如果焦点本身<code>canRequestFocus</code>是 false, 就返回了(debug 会报错我知道, 但是咱们以 release 运行结果为准).</p>
<p>然后如果父节点是空, 会设置一个<code>_requestFocusWhenReparented</code>为 true, 看名字应该是当重新附着的时候请求焦点, 换句话说先记账,我个人对于这里的理解是, 如果你开了新页面, 因为整个旧页面都失去了焦点, 但是当这个旧页面返回前台时, 检测这个东西是 true 时, 就会重新请求焦点.</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595467849.png" alt="image-20200723093049253"></p>
<p>把当前父焦点按顺序添加到末尾的意思, 具体作用不明</p>
<p>然后是</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-dart" data-lang="dart"><span style="color:#66d9ef">if</span> (hasPrimaryFocus <span style="color:#f92672">&amp;&amp;</span> (_manager._markedForFocus <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">||</span> _manager._markedForFocus <span style="color:#f92672">==</span> <span style="color:#66d9ef">this</span>)) {
  <span style="color:#66d9ef">return</span>;
}
</code></pre></div><p>这里的作用就是如果有有主焦点, 且已经被标记为当前焦点, 才 return, <code>hasPrimaryFocus</code>是表示就是当前主焦点. <code>_markedForFocus</code>表示虽然申请了, 但是暂时还没活动, 类似于一个脏标记, 这个变量应该是给下一帧使用的.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-dart" data-lang="dart">_hasKeyboardToken <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
<span style="color:#66d9ef">assert</span>(_focusDebug(<span style="color:#e6db74">&#39;Node requesting focus: </span><span style="color:#e6db74">$</span>this<span style="color:#e6db74">&#39;</span>));
_markNextFocus(<span style="color:#66d9ef">this</span>);
</code></pre></div><p>这东西理解起来就比较简单了, 获取键盘 token, 标记下一个焦点是自己</p>
<hr>
<p>然后, 来看看<code>FocusScopeNode</code>里这个方法的实现</p>
<p><!-- raw HTML omitted --></p>
<p>其实就是安排 manager 来做标记</p>
<h4 id="manager-的处理过程">manager 的处理过程</h4>
<p>刚刚有一个方法是<code>_markNextFocus</code>, 这个方法会调用到 manager 里, 我们来跟一下这个方法, 看看 manager 这个单例管理类里都干了什么<img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595475103.png" alt="image-20200723113143595"></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595475151.png" alt="image-20200723113231263"></p>
<p>如果是当前的主焦点, 就不管</p>
<p>如果不是, 则调用 update 方法</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595475240.png" alt="image-20200723113400797"></p>
<p>就是一个标记方法, 然后让程序在回调里执行 focusChange</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595475534.png" alt="2020-07-23 at 11.35 AM"></p>
<h2 id="分离detach">分离(detach)</h2>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595482645.png" alt="image-20200723133725482"></p>
<p>核心大概就是这些, 在<code>FocusAttachment</code>内,如果已经附着, 则<code>unfocus</code>一下, 然后到 manager 里标记分离, 解除父类和子类的关系, 接着置空<code>_node._attachment</code>. 可以理解为生命周期, 你在绑定做了什么, 到了解绑的时候自然要反向操作</p>
<h2 id="软键盘弹起的问题">软键盘弹起的问题</h2>
<p>有的同学看完了焦点相关的东西会感觉奇怪了, 怎么没有软键盘的东西, 我们讲了半天焦点, 没看见有软键盘弹起的逻辑啊?</p>
<p>其实这是因为前面的部分只是焦点部分, 我在前文中提及过, 在 flutter 中, 不止是文本输入框才能获取焦点, 而是任何控件(比如按钮)都存在理论上获得焦点的可能性.</p>
<h3 id="反向查找">反向查找</h3>
<p>如何反向查找呢? 我通过知识的积累(其实就是各种瞎看)知道了所有 flutter 和原生交互都是通过<code>SystemChannels</code>来进行的, 那我们来一路跟一跟</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595475925.png" alt="image-20200723114525795"></p>
<p>挖, 就是这个, 就是这个, 但是软键盘是通过 textInput 这东西过来的, 我们看看这东西是怎么实现的</p>
<h3 id="textinput">TextInput</h3>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476036.png" alt="image-20200723114716302"></p>
<p>好吧, 就是一个 channel, 但是这东西有什么方法呢, 我们看看调用和封装吧.</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476117.png" alt="image-20200723114837003"></p>
<p>有这么一个类专门封装了这东西, 正向调用和回调</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476160.png" alt="image-20200723114920368"></p>
<p>看名字就是编辑状态, action(软键盘的回车之类的?), 更新光标, 关闭. 这里先不细看</p>
<h4 id="updateeditingstate"><code>updateEditingState</code></h4>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476331.png" alt="image-20200723115211843"></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476282.png" alt="image-20200723115122526"></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476408.png" alt="image-20200723115328513"></p>
<p>这就和平时的<code>onChanged</code>回调对应上了</p>
<h4 id="performaction"><code>performAction</code></h4>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476510.png" alt="image-20200723115510148"></p>
<p>看名字基本就知道了, 对应的是各种键盘的回车键功能</p>
<h4 id="updatefloatingcursor"><code>updateFloatingCursor</code></h4>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476581.png" alt="image-20200723115621549"></p>
<p>基本就是因为点击之类或者别的什么原因, 光标位置要发生变化</p>
<h4 id="connectionclosed"><code>connectionClosed</code></h4>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595476647.png" alt="image-20200723115726998"></p>
<p>这个就没啥可说的了, 断开与软键盘连接的回调</p>
<h4 id="show"><code>show</code></h4>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1596098980.png" alt="image-20200723120022040"></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1596098989.png" alt="image-20200723120054204"></p>
<p>当控件有焦点时,, 就可以调用 TextInput 的<code>show</code>方法了, 这也就是软键盘弹出的逻辑了.</p>
<p>这时候又有同学要问了, 道理我都懂, 那为啥会触发到这个方法呢? 我说你这人怎么这么讨厌&hellip;</p>
<p>好吧, 从源码的角度来看, 也是有出处的</p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595482313.png" alt="image-20200723133152880"></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595482327.png" alt="image-20200723133207157"></p>
<p><img src="https://cdn.jsdelivr.net/gh/kikt-blog/blog-2@t1/uPic/1595482340.png" alt="image-20200723133220009"></p>
<p>经过这素质三连, 我觉得真相大白了, 本身<code>focusNode</code>是一个<code>ChangeNotifier</code>, 当这东西发生变化时, 自然会通知到回调方法(<code>_handleFocusChanged</code>), 然后我们知道之前的 manager 那边有方法会调用到这个 notify, 所以键盘自然就会弹起来了.</p>
<h2 id="后记">后记</h2>
<p>本篇主要是解析源码中的焦点和软键盘弹起的相关部分, 本篇系列文章也许还有第三篇, 也许没有, 一切随缘</p>
<p>以上.</p>

                        </div>

                        

<div class="post-archive">
    <h2>See Also</h2>
    <ul class="listing">
        
        <li><a href="/posts/flutter/focusnode-1/">Flutter FocusNode 焦点那点事-(一)</a></li>
        
        <li><a href="/posts/flutter/dart/generators/">dart中的生成器函数</a></li>
        
        <li><a href="/posts/flutter/route/navigator-helper2/">Navigator Helper2</a></li>
        
        <li><a href="/posts/flutter/grey-app/">flutter 怎么实现app整体灰度</a></li>
        
        <li><a href="/posts/flutter/plugin/flutter-sdk-import-aar/">Flutter 插件开发之引入aar到安卓部分 并使用本地maven</a></li>
        
    </ul>
</div>


                        <div class="post-meta meta-tags">
                            
                            <ul class="clearfix">
                                
                                <li><a href="http://www.kikt.top/tags/flutter">flutter</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/%E7%84%A6%E7%82%B9">焦点</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/focus">focus</a></li>
                                
                                <li><a href="http://www.kikt.top/tags/focusNode">focusNode</a></li>
                                
                            </ul>
                            
                        </div>
                    </article>
                    
    

    
    
    <div class="post bg-white">
      <script src="https://utteranc.es/client.js"
            repo= "caijinglong/kikt-blog-comment"
            issue-term="pathname"
            theme="github-light"
            crossorigin="anonymous"
            async>
      </script>
    </div>
    
                </div>
            </div>
            <div id="secondary">
    <section class="widget">
        <form id="search" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank" _lpchecked="1">
      
      <input type="text" name="q" maxlength="20" placeholder="Search">
      <input type="hidden" name="sitesearch" value="http://www.kikt.top">
      <button type="submit" class="submit icon-search"></button>
</form>
    </section>
    
    <section class="widget">
        <h3 class="widget-title">最近文章</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://www.kikt.top/posts/java/crack/crack1/" title="使用javassist,修改jar包方法实现">使用javassist,修改jar包方法实现</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/github/actions/create/" title="Github action 的开发到发布">Github action 的开发到发布</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/jenkins&#43;fastlane/" title="flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英">flutter Jenkins&#43;fastlane 自动化打测试包, 并上传蒲公英</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/go/compile-for-android/" title="编译 go 源码为 android 动态库(so)">编译 go 源码为 android 动态库(so)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/focusnode-2/" title="Flutter FocusNode 焦点那点事-(二)">Flutter FocusNode 焦点那点事-(二)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/focusnode-1/" title="Flutter FocusNode 焦点那点事-(一)">Flutter FocusNode 焦点那点事-(一)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/server/caddy/cros_for_local_dev/" title="用 Caddy 解决 web 开发中本地跨域的问题">用 Caddy 解决 web 开发中本地跨域的问题</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/route/navigator-helper2/" title="Navigator Helper2">Navigator Helper2</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/grey-app/" title="flutter 怎么实现app整体灰度">flutter 怎么实现app整体灰度</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/posts/flutter/plugin/flutter-sdk-import-aar/" title="Flutter 插件开发之引入aar到安卓部分 并使用本地maven">Flutter 插件开发之引入aar到安卓部分 并使用本地maven</a>
    </li>
    
</ul>
    </section>

    

    <section class="widget">
        <h3 class="widget-title">分类</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://www.kikt.top/categories/android/">android(25)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/caddy/">caddy(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/dart/">dart(5)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/docker/">docker(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/flutter/">flutter(60)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/github/">github(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/go/">go(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/golang/">golang(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/iOS/">iOS(6)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/ios/">ios(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/java/">java(6)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/jetbrains/">jetbrains(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/other/">other(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/python/">python(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/server/">server(5)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/spring/">spring(3)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/tools/">tools(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/%E6%9D%82%E9%A1%B9/">杂项(1)</a>
    </li>
    
    <li>
        <a href="http://www.kikt.top/categories/%E6%B6%82%E9%B8%A6/">涂鸦(1)</a>
    </li>
    
</ul>
    </section>

    <section class="widget">
        <h3 class="widget-title">标签</h3>
<div class="tagcloud">
    
    <a href="http://www.kikt.top/tags/10/">10</a>
    
    <a href="http://www.kikt.top/tags/2.3/">2.3</a>
    
    <a href="http://www.kikt.top/tags/Clipboard/">Clipboard</a>
    
    <a href="http://www.kikt.top/tags/Javassist/">Javassist</a>
    
    <a href="http://www.kikt.top/tags/Migrate/">Migrate</a>
    
    <a href="http://www.kikt.top/tags/aar/">aar</a>
    
    <a href="http://www.kikt.top/tags/actions/">actions</a>
    
    <a href="http://www.kikt.top/tags/aidl/">aidl</a>
    
    <a href="http://www.kikt.top/tags/android/">android</a>
    
    <a href="http://www.kikt.top/tags/androidQ/">androidQ</a>
    
    <a href="http://www.kikt.top/tags/androidX/">androidX</a>
    
    <a href="http://www.kikt.top/tags/apk/">apk</a>
    
    <a href="http://www.kikt.top/tags/app/">app</a>
    
    <a href="http://www.kikt.top/tags/appcode/">appcode</a>
    
    <a href="http://www.kikt.top/tags/azure/">azure</a>
    
    <a href="http://www.kikt.top/tags/ble/">ble</a>
    
    <a href="http://www.kikt.top/tags/bottomsheet/">bottomsheet</a>
    
    <a href="http://www.kikt.top/tags/butterknife/">butterknife</a>
    
    <a href="http://www.kikt.top/tags/c/">c</a>
    
    <a href="http://www.kikt.top/tags/caddy/">caddy</a>
    
    <a href="http://www.kikt.top/tags/camera/">camera</a>
    
    <a href="http://www.kikt.top/tags/cameraX/">cameraX</a>
    
    <a href="http://www.kikt.top/tags/channel/">channel</a>
    
    <a href="http://www.kikt.top/tags/cocoapods/">cocoapods</a>
    
    <a href="http://www.kikt.top/tags/cupertino/">cupertino</a>
    
    <a href="http://www.kikt.top/tags/dart/">dart</a>
    
    <a href="http://www.kikt.top/tags/dart-2.6/">dart-2.6</a>
    
    <a href="http://www.kikt.top/tags/desktop/">desktop</a>
    
    <a href="http://www.kikt.top/tags/dialog/">dialog</a>
    
    <a href="http://www.kikt.top/tags/dmg/">dmg</a>
    
    <a href="http://www.kikt.top/tags/doc/">doc</a>
    
    <a href="http://www.kikt.top/tags/docker/">docker</a>
    
    <a href="http://www.kikt.top/tags/docker-compose/">docker-compose</a>
    
    <a href="http://www.kikt.top/tags/excel/">excel</a>
    
    <a href="http://www.kikt.top/tags/exists/">exists</a>
    
    <a href="http://www.kikt.top/tags/faq/">faq</a>
    
    <a href="http://www.kikt.top/tags/fastlane/">fastlane</a>
    
    <a href="http://www.kikt.top/tags/ffi/">ffi</a>
    
    <a href="http://www.kikt.top/tags/ffmpeg/">ffmpeg</a>
    
    <a href="http://www.kikt.top/tags/flexmark/">flexmark</a>
    
    <a href="http://www.kikt.top/tags/flutter/">flutter</a>
    
    <a href="http://www.kikt.top/tags/flutter-web/">flutter-web</a>
    
    <a href="http://www.kikt.top/tags/focus/">focus</a>
    
    <a href="http://www.kikt.top/tags/focusNode/">focusNode</a>
    
    <a href="http://www.kikt.top/tags/form/">form</a>
    
    <a href="http://www.kikt.top/tags/framework/">framework</a>
    
    <a href="http://www.kikt.top/tags/github/">github</a>
    
    <a href="http://www.kikt.top/tags/go/">go</a>
    
    <a href="http://www.kikt.top/tags/golang/">golang</a>
    
    <a href="http://www.kikt.top/tags/gradle/">gradle</a>
    
    <a href="http://www.kikt.top/tags/http/">http</a>
    
    <a href="http://www.kikt.top/tags/iOS/">iOS</a>
    
    <a href="http://www.kikt.top/tags/ide/">ide</a>
    
    <a href="http://www.kikt.top/tags/idea/">idea</a>
    
    <a href="http://www.kikt.top/tags/index/">index</a>
    
    <a href="http://www.kikt.top/tags/inside/">inside</a>
    
    <a href="http://www.kikt.top/tags/ios/">ios</a>
    
    <a href="http://www.kikt.top/tags/java/">java</a>
    
    <a href="http://www.kikt.top/tags/jenkins/">jenkins</a>
    
    <a href="http://www.kikt.top/tags/jetbrains/">jetbrains</a>
    
    <a href="http://www.kikt.top/tags/jsdelivr/">jsdelivr</a>
    
    <a href="http://www.kikt.top/tags/json/">json</a>
    
    <a href="http://www.kikt.top/tags/json_serializable/">json_serializable</a>
    
    <a href="http://www.kikt.top/tags/junit/">junit</a>
    
    <a href="http://www.kikt.top/tags/library/">library</a>
    
    <a href="http://www.kikt.top/tags/live-template/">live-template</a>
    
    <a href="http://www.kikt.top/tags/loadmore/">loadmore</a>
    
    <a href="http://www.kikt.top/tags/log/">log</a>
    
    <a href="http://www.kikt.top/tags/mac/">mac</a>
    
    <a href="http://www.kikt.top/tags/macos/">macos</a>
    
    <a href="http://www.kikt.top/tags/maven/">maven</a>
    
    <a href="http://www.kikt.top/tags/mysql/">mysql</a>
    
    <a href="http://www.kikt.top/tags/navigationbar/">navigationbar</a>
    
    <a href="http://www.kikt.top/tags/navigator/">navigator</a>
    
    <a href="http://www.kikt.top/tags/ndk/">ndk</a>
    
    <a href="http://www.kikt.top/tags/nginx/">nginx</a>
    
    <a href="http://www.kikt.top/tags/oc/">oc</a>
    
    <a href="http://www.kikt.top/tags/opencv/">opencv</a>
    
    <a href="http://www.kikt.top/tags/poi/">poi</a>
    
    <a href="http://www.kikt.top/tags/pub/">pub</a>
    
    <a href="http://www.kikt.top/tags/python/">python</a>
    
    <a href="http://www.kikt.top/tags/retrofit/">retrofit</a>
    
    <a href="http://www.kikt.top/tags/route/">route</a>
    
    <a href="http://www.kikt.top/tags/server/">server</a>
    
    <a href="http://www.kikt.top/tags/so/">so</a>
    
    <a href="http://www.kikt.top/tags/spp/">spp</a>
    
    <a href="http://www.kikt.top/tags/sprintboot/">sprintboot</a>
    
    <a href="http://www.kikt.top/tags/swift/">swift</a>
    
    <a href="http://www.kikt.top/tags/tap/">tap</a>
    
    <a href="http://www.kikt.top/tags/thymeleaf/">thymeleaf</a>
    
    <a href="http://www.kikt.top/tags/tips/">tips</a>
    
    <a href="http://www.kikt.top/tags/toast/">toast</a>
    
    <a href="http://www.kikt.top/tags/transition/">transition</a>
    
    <a href="http://www.kikt.top/tags/ui/">ui</a>
    
    <a href="http://www.kikt.top/tags/utf8/">utf8</a>
    
    <a href="http://www.kikt.top/tags/yield/">yield</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%8C%E8%BF%9B%E5%88%B6/">二进制</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89/">交叉</a>
    
    <a href="http://www.kikt.top/tags/%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/">交叉编译</a>
    
    <a href="http://www.kikt.top/tags/%E4%BB%8B%E7%BB%8D/">介绍</a>
    
    <a href="http://www.kikt.top/tags/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/">代码规范</a>
    
    <a href="http://www.kikt.top/tags/%E5%86%B2%E7%AA%81/">冲突</a>
    
    <a href="http://www.kikt.top/tags/%E5%8A%A8%E6%80%81/">动态</a>
    
    <a href="http://www.kikt.top/tags/%E5%8F%91%E5%B8%83/">发布</a>
    
    <a href="http://www.kikt.top/tags/%E5%8F%AF%E6%89%A7%E8%A1%8C/">可执行</a>
    
    <a href="http://www.kikt.top/tags/%E5%9B%BE%E5%BA%8A/">图床</a>
    
    <a href="http://www.kikt.top/tags/%E5%9F%8E%E5%B8%82/">城市</a>
    
    <a href="http://www.kikt.top/tags/%E5%A4%A7%E6%96%87%E4%BB%B6/">大文件</a>
    
    <a href="http://www.kikt.top/tags/%E5%B7%A5%E5%85%B7/">工具</a>
    
    <a href="http://www.kikt.top/tags/%E5%B7%B2%E6%9C%89%E9%A1%B9%E7%9B%AE/">已有项目</a>
    
    <a href="http://www.kikt.top/tags/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/">开发环境</a>
    
    <a href="http://www.kikt.top/tags/%E6%80%BB%E7%BB%93/">总结</a>
    
    <a href="http://www.kikt.top/tags/%E6%85%A2/">慢</a>
    
    <a href="http://www.kikt.top/tags/%E6%89%93%E5%8C%85/">打包</a>
    
    <a href="http://www.kikt.top/tags/%E6%89%AB%E7%A0%81/">扫码</a>
    
    <a href="http://www.kikt.top/tags/%E6%8F%92%E4%BB%B6/">插件</a>
    
    <a href="http://www.kikt.top/tags/%E6%96%87%E6%A1%A3/">文档</a>
    
    <a href="http://www.kikt.top/tags/%E6%A8%A1%E6%9D%BF/">模板</a>
    
    <a href="http://www.kikt.top/tags/%E6%B8%B2%E6%9F%93/">渲染</a>
    
    <a href="http://www.kikt.top/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">源码解析</a>
    
    <a href="http://www.kikt.top/tags/%E7%81%B0%E5%BA%A6/">灰度</a>
    
    <a href="http://www.kikt.top/tags/%E7%82%B9%E5%87%BB/">点击</a>
    
    <a href="http://www.kikt.top/tags/%E7%84%A6%E7%82%B9/">焦点</a>
    
    <a href="http://www.kikt.top/tags/%E7%88%AC%E8%99%AB/">爬虫</a>
    
    <a href="http://www.kikt.top/tags/%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86/">状态管理</a>
    
    <a href="http://www.kikt.top/tags/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/">百度地图</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E7%A0%81/">编码</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E7%A8%8B%E6%8A%80%E5%B7%A7/">编程技巧</a>
    
    <a href="http://www.kikt.top/tags/%E7%BC%96%E8%AF%91/">编译</a>
    
    <a href="http://www.kikt.top/tags/%E7%BF%BB%E8%AF%91/">翻译</a>
    
    <a href="http://www.kikt.top/tags/%E8%93%9D%E7%89%99/">蓝牙</a>
    
    <a href="http://www.kikt.top/tags/%E8%A1%A8%E5%8D%95/">表单</a>
    
    <a href="http://www.kikt.top/tags/%E8%B7%AF%E7%94%B1/">路由</a>
    
    <a href="http://www.kikt.top/tags/%E8%BF%87%E5%9C%BA%E5%8A%A8%E7%94%BB/">过场动画</a>
    
    <a href="http://www.kikt.top/tags/%E9%9A%90%E7%A7%81/">隐私</a>
    
</div>
    </section>

    
<section class="widget">
    <h3 class="widget-title">友情链接</h3>
    <ul class="widget-list">
        
        <li>
            <a target="_blank" href="https://github.com/Caijinglong" title="我的Github">我的Github</a>
        </li>
        
        <li>
            <a target="_blank" href="https://flutter.dev" title="flutter 官网">flutter 官网</a>
        </li>
        
    </ul>
</section>


    <section class="widget">
        <h3 class="widget-title">其它</h3>
        <ul class="widget-list">
            <li><a href="http://www.kikt.top/index.xml">文章 RSS</a></li>
        </ul>
    </section>
</div>
        </div>
    </div>
</div>
<footer id="footer">
    <div class="container">
        <div>
            <p>如果我的blog对您有帮助,而您愿意捐赠 😆, 可以用下面的二维码</p>
            <img style="width:5rem;" src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424141834.png"/>
            <img style="width:5rem; padding-left: 2rem;" 
            src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424141931.png"/>
            <img style="width:5rem; padding-left: 2rem;" 
            src="https://dev.azure.com/cjlspy/844861b8-ee45-48a3-b430-b974684baaaa/_apis/git/repositories/bf4da1c1-1cfa-4f54-8a75-745cce2b737c/items?versionDescriptor%5Bversion%5D=master&versionDescriptor%5BversionOptions%5D=0&versionDescriptor%5BversionType%5D=0&resolveLfs=true&%24format=octetStream&api-version=5.0&path=%2F20190424142013.png"/>
        </div>
        &copy; 2018 <a href="http://www.kikt.top">caijinglong的博客 By caijinglong</a>.
        Powered by <a rel="nofollow noreferer noopener" href="https://gohugo.io" target="_blank">Hugo</a>.
        <a href="http://www.flysnow.org/" target="_blank">Theme</a> based on <a href="https://github.com/rujews/maupassant-hugo" target="_blank">maupassant</a>.<br/>
        本站所有内容基于<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" style="color:coral">CC4.0</a>协议发布,需要转载必须署名
        <br/>
        如果有问题可以<a href="mailto:cjl_spy@163.com">email联系我</a>
        <br/>
        京ICP备18038473号-1
        
    </div>
</footer>


    <script type="text/javascript" src="/js/prism.js" async="true"></script>
    <script type="text/javascript">
    
    (function(){
        $("pre code").parent().addClass("line-numbers")
    }())

    window.MathJax = {
        tex2jax: {
            inlineMath: [ ['$','$'] ],
            processEscapes: true
        }
    };
    </script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script>

<a id="rocket" href="#top"></a>
<script type="text/javascript" src="/js/totop.js?v=0.0.0" async=""></script>



<script type="text/javascript" src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script>




</body>
</html>
